/////////////////////////////////////////////////////////////
// CLASSES: 
//	- tc_node, p_msg, r_msg
// FUNCTIONS:
//	- itos()
//	- char_hton(), char_ntoh()
//	- From iwlib.h: iw_ether_ntop(), iw_ether_ntoa(), iw_ether_aton()

#ifndef TC_TYPES_HPP
#define TC_TYPES_HPP

#include <iostream> // std::cout, std::endl
#include <sstream> // to define itos()
#include <netinet/in.h> // sockaddr_in, htonl(), htons()
#include <errno.h> // constants: E2BIG, EINVAL
#include <net/ethernet.h> // struct ether_addr
#include <arpa/inet.h> // inet_aton()

#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif

#ifndef P_MSG
#define P_MSG 0
#endif

#ifndef R_MSG
#define R_MSG 1
#endif

using std::string;
using std::stringstream;


//////////////////////////////////////////////////////
// Convert an integer to string
string itos( int i ){stringstream s; s << i; return s.str();}

//////////////////////////////////////////////////////
// Copy the data in hostptr to the buffer using network byte-order
// Note that len = 6 means a char[] of len=6
void char_hton(void* buffer, const void* hostptr, int len){
  if( !( (len == 2) || (len == 4) || (len == 6) ) ){
    std::cout << "char_hton(): unsupported type." << std::endl;
    exit(1);
  }
  if(len == 2){
    uint16_t hostshort; memcpy(&hostshort, (uint16_t *) hostptr, len);
    uint16_t netshort = htons(hostshort); memcpy(buffer, &netshort, len);
    return;
  }
  if(len == 4){
    uint32_t hostlong; memcpy(&hostlong, (uint32_t *) hostptr, len);
    uint32_t netlong = htonl(hostlong); memcpy(buffer, &netlong, len);
    return;
  }
  if(len == 6){
    memcpy(buffer, (char *) hostptr, 6);
    return;
  }
}

//////////////////////////////////////////////////////
// Copy the data in buffer to hostptr using host byte-order
void char_ntoh(const void* buffer, void* hostptr, int len){
  if( !( (len == 2) || (len == 4) || (len == 6) ) ){
    std::cout << "char_ntoh(): unsupported type." << std::endl;
    exit(1);
  }
  if(len == 2){
    uint16_t netshort; memcpy(&netshort, buffer, len);
    uint16_t hostshort = ntohs(netshort);
    memcpy((uint16_t *) hostptr, &hostshort, len);
    return;
  }
  if(len == 4){
    uint32_t netlong; memcpy(&netlong, buffer, len);
    uint32_t hostlong = ntohl(netlong);
    memcpy((uint32_t *) hostptr, &hostlong, len);
    return;
  }
  if(len == 6){
    memcpy((char *) hostptr, buffer, 6);
    return;
  }
}


//////////////////////////////////////////////////////
// FROM iwlib.h

//////////
// Display an Ethernet address in readable format.

void tc_ether_ntop(const struct ether_addr* eth, char* buf)
{
  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
          eth->ether_addr_octet[0], eth->ether_addr_octet[1],
          eth->ether_addr_octet[2], eth->ether_addr_octet[3],
          eth->ether_addr_octet[4], eth->ether_addr_octet[5]);
}

// Display an Ethernet address in readable format.
// Same with a static buffer

char* tc_ether_ntoa(const struct ether_addr* eth)
{
  static char buf[20];
  tc_ether_ntop(eth, buf);
  return buf;
}

/////////
// Input an arbitrary length MAC address and convert to binary.
// Return address size.

int tc_ether_aton(const char* orig, struct ether_addr* eth)
{
  const char* bufp;
  int i = 0;

  for (bufp = orig; *bufp != '\0'; ++bufp) {
    unsigned int val;
    unsigned char c = *bufp++;
    if (isdigit(c)) val = c - '0';
    else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
    else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
    else break;

    val <<= 4;
    c = *bufp++;
    if (isdigit(c)) val |= c - '0';
    else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10;
    else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10;
    else break;

    eth->ether_addr_octet[i] = (unsigned char) (val & 0377);

    if(++i == ETH_ALEN) { // That's it.  Any trailing junk?
	return 1;
    }

    if (*bufp != ':'){
	break;
    }
  }
  errno = EINVAL;
  return 0;
}


/////////////////////////////////////////////////////////////
// TC_NODE class
// Holds node-related information
// Note that in_addr_t is equivalent to uint32_t (see inttypes.h)

// WARNING: if the tc_node structure should change, almost all member
//	     functions have to be modified accordingly.

struct tc_node{
  in_addr_t IP_ADDR; // Source IP address
  struct ether_addr MAC_ADDR; // Source MAC address
  //Note: ether_addr has a 6-byte member, u_char octet[6], but its size is actually 8 bytes
  uint16_t TPC; //0: not TPC capable; 1: TPC capable
  uint16_t MAX_POWER; // Rounded maximum transmit power (in dBm)

  // Default constructor
  tc_node() {memset(this, '0', sizeof(struct tc_node));}

  // Copy constructor
  tc_node(const struct tc_node& node);

  // Initialize node structure
  void init(const char* ip_addr, const char* mac_addr,
	    bool tpc, int max_power);

  void get(string& ip_addr, string& mac_addr,
	    bool& tpc, int& max_power);

  // Initialize node structure using a single c-string buffer
  // (buf should contain data in network byte order)
  ssize_t init(const char* buf);

  // Get the number of bytes of data stored in the structure
  // note that sizeof(tc_node) == 16, but bytes() returns only 14
  int size(){return (sizeof(IP_ADDR) +
		     sizeof(MAC_ADDR) +
		     sizeof(TPC) +
		     sizeof(MAX_POWER));}
  
  // Destructor
  ~tc_node(){};

  // Overloaded operators:
  struct tc_node& operator= (const struct tc_node& node);
  int operator== (const struct tc_node& nodeB) const;
  int operator< (const struct tc_node& nodeB) const;
};

//////////
tc_node::tc_node(const struct tc_node& node)
{
  IP_ADDR = node.IP_ADDR;
  MAC_ADDR = node.MAC_ADDR;
  TPC = node.TPC;
  MAX_POWER = node.MAX_POWER;
}

//////////
void tc_node::init(const char* ip_addr, const char* mac_addr,
		   bool tpc, int max_power)
{
  struct in_addr src_addr;
  if( inet_aton( ip_addr, &src_addr ) == 0 ){
    std::cout << "tc_node: Error converting inet address" << std::endl;
    exit(1);
  }
  IP_ADDR = ntohl(src_addr.s_addr); // inet_aton returns in net byte order
  struct ether_addr eth;
  tc_ether_aton(mac_addr, &eth); // from iwlib.h
  memcpy(&MAC_ADDR, &eth, sizeof(struct ether_addr));
  TPC = (tpc ? 1 : 0);
  MAX_POWER = max_power;
}

//////////
ssize_t tc_node::init(const char* buf)
{
  ssize_t buflen = 0; // Buffer counter

  char_ntoh(&buf[buflen], &this->IP_ADDR, 4); buflen += 4;
  char_ntoh(&buf[buflen], &this->MAC_ADDR, 6); buflen += 6;
  char_ntoh(&buf[buflen], &this->TPC, 2); buflen += 2;
  char_ntoh(&buf[buflen], &this->MAX_POWER, 2); buflen += 2;

  return buflen;
}

//////////
void tc_node::get(string& ip_addr, string& mac_addr,
		   bool& tpc, int& max_power)
{
  // Remember that data in tc_node is stored in host byte order
  
  // IP address
  struct in_addr in;
  char_hton(&in.s_addr, &IP_ADDR, 4); // convert IP to network byte order
  ip_addr.assign( inet_ntoa(in) );
  
  // MAC address
  mac_addr.assign( tc_ether_ntoa(&MAC_ADDR) );

  // Debugging...
  //std::cout << "IP = " << ip_addr << std::endl;
  //std::cout << "MAC = " << mac_addr << std::endl;

  // TPC
  tpc = (TPC ? true : false);

  // MAX_POWER
  max_power = (int) MAX_POWER;
}

////////////
struct tc_node& tc_node::operator= (const struct tc_node& node)
{
  if(this != &node)
    memcpy(this, &node, sizeof(tc_node));
  return *this;
}

///////////
int tc_node::operator== (const struct tc_node& nodeB) const
{
  if( this->IP_ADDR != nodeB.IP_ADDR ){
    return 0;
  }
  if( memcmp(&this->MAC_ADDR,
	     &nodeB.MAC_ADDR,
	     6) != 0 ){
    return 0;
  }

  if( this->TPC != nodeB.TPC ){
    return 0;
  }
  
  if( this->MAX_POWER != nodeB.MAX_POWER ){
    return 0;
  }

  return 1;
}

///////////
int tc_node::operator< (const struct tc_node& nodeB) const
{
  if( this->IP_ADDR < nodeB.IP_ADDR )
    return 1;
  else
    return 0;
}


/////////////////////////////////////////////////////////////
// P_MSG definition
typedef struct {
  uint16_t TYPE; // Type of message
  tc_node NODE; // Node information
} p_msg;


/////////////////////////////////////////////////////////////
// R_MSG definition
typedef struct {
  uint16_t TYPE; // Type of message
  uint16_t LEN; // Number of tc_node elements in the R_MSG
                // Note that the 1st element is the source
                // Thus, LEN = 1 + number_of_neighbors
  tc_node* NEIGHBOR; // List of neighbors
  int16_t* WEIGHT; // List of edge weights

} r_msg;


#endif // TC_TYPES_HPP
